package global

import (
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"git.oschina.net/YPTArch/km_api_dispatcher/sys/logs"
	"os"
	"runtime"
	"strconv"
	"strings"
	"time"
)

//Config ...
type Config struct {
	App struct {
		Name   string
		AppID  string
		AppKey string
	}
	Service struct {
		Name string
		Port int
	}
	Log struct {
		Host     string
		Port     int
		Database string
	}
	DBDriver struct {
		Host            string
		User            string
		Password        string
		Database        string
		Charset         string
		Port            int
		ConnectionLimit int
	}
	KM struct {
		URL string
	}
}

var coder = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")

//Conf ...
var Conf Config

var dbDriver *mysql

//Log ...
//var Log *logger.Logger

func getStringFromObj(obj interface{}, keys ...string) string {
	switch obj.(type) {
	case string:
		if len(keys) == 0 {
			return obj.(string)
		}
	case map[string]interface{}:
		m := obj.(map[string]interface{})
		if v, e := m[keys[0]]; e {
			return getStringFromObj(v, keys[1:]...)
		}
	}
	panic(fmt.Errorf("cannot get string from map with key -> %s", keys[0]))
}

func getFloat64FromObj(obj interface{}, keys ...string) float64 {
	switch obj.(type) {
	case float64:
		if len(keys) == 0 {
			return obj.(float64)
		}
	case map[string]interface{}:
		m := obj.(map[string]interface{})
		if v, e := m[keys[0]]; e {
			return getFloat64FromObj(v, keys[1:]...)
		}
	}
	panic(fmt.Errorf("cannot get float64 from map with key -> %s", keys[0]))
}

func init() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println(r)
		}
	}()
	//os.Setenv("KMY_APID_CONFIG", "eyJBcHAiOnsiTmFtZSI6InRlc3QiLCJBcHBJZCI6ImFwcF8xNTRjZjBiOTRjM2IzNWU5IiwiQXBwS2V5IjoiKkIxOTA2OUIzREU2QTRFNDIxRjBCRDE5QkE2RkQzNUEwOENFMDM3RjUifSwiU2VydmljZSI6eyJOYW1lIjoidGVzdCIsIlBvcnQiOjMyMzMzLCJBcHBLZXkiOiIqQjE5MDY5QjNERTZBNEU0MjFGMEJEMTlCQTZGRDM1QTA4Q0UwMzdGNSJ9LCJLbXlBUEkiOnsiSG9zdCI6IjE5Mi4xNjguMS4xOTQiLCJQb3J0Ijo3ODc4fSwiTG9nIjp7IkRhdGFiYXNlIjoibW9uZ28iLCJIb3N0IjoiMTkyLjE2OC4xLjIyOSIsIlBvcnQiOjI3MDE3fSwiTXlTcWwiOlt7InJlc25hbWUiOiLova/ku7bmna/mlbDmja7lupMiLCJ0eXBlIjoibXlzcWwiLCJob3N0IjoiMTkyLjE2OC4xLjIyOSIsInVzZXIiOiJyb290IiwicGFzc3dvcmQiOiJhYmNkMTIzNCIsImRhdGFiYXNlIjoic29mdHdhcmVfY3VwMiIsInBvcnQiOjMzMDYsImNvbm5lY3Rpb25MaW1pdCI6MTAwfV19")
	//os.Setenv("KMY_APID_CONFIG", "eyJBcHAiOnsiTmFtZSI6InNvZnR3YXJlX2N1cCIsIkFwcElkIjoiYXBwXzQ4MzAzMDY0M2M4ZWU4ZjUiLCJBcHBLZXkiOiIqMzRBQjYwRDE0MDkwMzhDQzJCMEYxMDk4MDYxOEJGNTg1M0Q0RDU2NyJ9LCJTZXJ2aWNlIjp7Ik5hbWUiOiJtYW5hZ2UiLCJQb3J0IjozMDA4OSwiQXBwS2V5IjoiKjM0QUI2MEQxNDA5MDM4Q0MyQjBGMTA5ODA2MThCRjU4NTNENEQ1NjcifSwiS215QVBJIjp7Ikhvc3QiOiIxOTIuMTY4LjEuMTk0IiwiUG9ydCI6Nzg3OH0sIkxvZyI6eyJEYXRhYmFzZSI6Im1vbmdvIiwiSG9zdCI6IjE5Mi4xNjguMS4yMjkiLCJQb3J0IjoyNzAxN30sIk15U3FsIjpbeyJyZXNuYW1lIjoi6L2v5Lu25p2v5pWw5o2u5bqTIiwidHlwZSI6Im15c3FsIiwiaG9zdCI6IjE5Mi4xNjguMS4yMjkiLCJ1c2VyIjoicm9vdCIsInBhc3N3b3JkIjoiYWJjZDEyMzQiLCJkYXRhYmFzZSI6InNvZnR3YXJlX2N1cDIiLCJwb3J0IjozMzA2LCJjb25uZWN0aW9uTGltaXQiOjEwMH1dfQ==")
	//30701----> os.Setenv("KMY_APID_CONFIG", "eyJBcHAiOnsiTmFtZSI6IuWMl+iPnC5FUlAiLCJBcHBJZCI6ImFwcF80N2FmMzA4MDIxN2JlM2QyIiwiQXBwS2V5IjoiKkJDRkFCMjQzNUQwRUUyQjhCMjQ0QUJGRTM3MDQwRjc0NzBFMUEwRDgifSwiU2VydmljZSI6eyJOYW1lIjoicHVyY2hhc2VzZXJ2aWNlIiwiUG9ydCI6MzA3MDEsIkFwcEtleSI6IipCQ0ZBQjI0MzVEMEVFMkI4QjI0NEFCRkUzNzA0MEY3NDcwRTFBMEQ4In0sIktteUFQSSI6eyJIb3N0IjoiMTkyLjE2OC4xLjE5NCIsIlBvcnQiOjc4Nzh9LCJMb2ciOnsiRGF0YWJhc2UiOiJtb25nbyIsIkhvc3QiOiIxOTIuMTY4LjEuMjI5IiwiUG9ydCI6MjcwMTd9LCJNeVNxbCI6W3sicmVzbmFtZSI6ImVycGRiX3Jlc291cmNlIiwidHlwZSI6Im15c3FsIiwiaG9zdCI6IjQyLjEyMy4xMjYuMTc4IiwidXNlciI6InJvb3QiLCJwYXNzd29yZCI6ImFiY2QxMjM0IiwiZGF0YWJhc2UiOiJFUlAiLCJwb3J0IjozMzA2LCJjb25uZWN0aW9uTGltaXQiOjEwMH1dfQ==")
	//os.Setenv("KM_URL", "192.168.1.194:7878")

	if ds, err := coder.DecodeString(strings.Replace(os.Getenv("KMY_APID_CONFIG"), " ", "+", -1)); err == nil {
		fmt.Println(string(ds))
		var v map[string]interface{}
		err = json.Unmarshal(ds, &v)
		if err != nil {
			fmt.Println(err)
		}
		// ============== Config App ==============
		Conf.App.Name = getStringFromObj(v, "App", "Name")
		Conf.App.AppID = getStringFromObj(v, "App", "AppId")
		Conf.App.AppKey = getStringFromObj(v, "App", "AppKey")
		// ============== Config Service ==============
		Conf.Service.Name = getStringFromObj(v, "Service", "Name")
		Conf.Service.Port = int(getFloat64FromObj(v, "Service", "Port"))
		// ============== Config Log ==============
		Conf.Log.Database = "mongo" //getStringFromObj(v, "Log", "Database")
		Conf.Log.Host = getStringFromObj(v, "Log", "Host")
		// Conf.Log.Host = "192.168.1.229"
		Conf.Log.Port = int(getFloat64FromObj(v, "Log", "Port"))
		// -------------- Create Log --------------
/*		var newLoggerErr error
		// Log, newLoggerErr = logger.NewLogger(Conf.Log.Database, Conf.Log.Host, Conf.Log.Port, "log", Conf.App.Name, Conf.Service.Name)

		if newLoggerErr != nil {
			panic(newLoggerErr)
		}*/
		// ============== Config DBDriver ==============
		if mysqlConfigs, e := v["MySql"]; e {
			if len(mysqlConfigs.([]interface{})) > 0 {
				Conf.DBDriver.Host = getStringFromObj(mysqlConfigs.([]interface{})[0], "host")
				Conf.DBDriver.User = getStringFromObj(mysqlConfigs.([]interface{})[0], "user")
				Conf.DBDriver.Password = getStringFromObj(mysqlConfigs.([]interface{})[0], "password")
				Conf.DBDriver.Database = getStringFromObj(mysqlConfigs.([]interface{})[0], "database")
				Conf.DBDriver.Charset = "utf8" //getStringFromObj(sqlserverConfigs.([]interface{})[0], "charset")
				Conf.DBDriver.Port = int(getFloat64FromObj(mysqlConfigs.([]interface{})[0], "port"))
				Conf.DBDriver.ConnectionLimit = int(getFloat64FromObj(mysqlConfigs.([]interface{})[0], "connectionLimit"))
			}
		} else {
			panic(errors.New("Error : Config DBDriver Failed -> Missing Env"))
		}
		// -------------- Create SQLServer --------------
		dbDriver = newMySQL(Conf.DBDriver.User, Conf.DBDriver.Password, Conf.DBDriver.Host, Conf.DBDriver.Port,
			Conf.DBDriver.Database, Conf.DBDriver.Charset, Conf.DBDriver.ConnectionLimit)
		// ============== Log Memery Stats ==============
		// go logMemStats()
		// ============== Update API List ==============
		go updateAPIList()
	}

	Conf.KM.URL = os.Getenv("KM_URL")

	// fmt.Println(" ----------------------------------------------------- ")
	// Log.Trace(fmt.Sprintf("Read ENV:[KM_URL] %s", Conf.KM.URL))
	// Log.Trace(strings.Replace(os.Getenv("KMY_APID_CONFIG"), " ", "+", -1))
	// fmt.Println(" ----------------------------------------------------- ")
}

func logMemStats() {
	for {
		var ms runtime.MemStats
		runtime.ReadMemStats(&ms)
		logs.Logger.Debug("Alloc[" + uint64ToString(ms.Alloc, "") + "] " +
			"TotalAlloc[" + uint64ToString(ms.TotalAlloc, "") + "] " +
			"HeapAlloc[" + uint64ToString(ms.HeapAlloc, "") + "] " +
			"HeapSys[" + uint64ToString(ms.HeapSys, "") + "] " +
			"HeapReleased[" + uint64ToString(ms.HeapReleased, "") + "]")
		time.Sleep(10 * time.Second)
	}
}

func uint64ToString(num uint64, unit string) (s string) {
	newNum := num
	if (num >> 20) > 0 {
		for (newNum >> 20) > 0 {
			newNum = (newNum >> 10)
			switch unit {
			case "":
				unit = "K"
			case "K":
				unit = "M"
			case "M":
				unit = "G"
			case "G":
				unit = "T"
			default:
				break
			}
		}
	}
	s = strconv.FormatUint(newNum, 10) + unit
	return
}
